package astudy.bytecode;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;

import astudy.bytecode.bcel.ProfilerWrapper;

/**
 * wrapper method in some classes before load them. 
 * @author wong.tong@gmail.com
 *
 */
public class AOPClassLoader extends URLClassLoader {

	private List<MethodFilter> filters = new ArrayList<MethodFilter>();
	
	public void appendFilter(MethodFilter filter) {
		this.filters.add(filter);
	}
	
    protected AOPClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }
    
    public Class<?> loadClass(String name) throws ClassNotFoundException {
    	
        return super.loadClass(name);
    }
    
    protected Class<?> findClass(String name) throws ClassNotFoundException {

    	byte[] bytes = null;
    	for (MethodFilter filter: filters) {
    		if (filter.getClassNamePattern().match(name)) {
        		InputStream is = null;
       	    	try {
       	    		int index = name.lastIndexOf('.');
       	    		String simpleClassName = name.substring(index+1);
       	    		String classFileName = simpleClassName + ".class";
       	    		
                	ProfilerWrapper bind = new ProfilerWrapper();
                	URL url = Shell.class.getResource(classFileName);
                	File classFile = new File(URLDecoder.decode(url.getPath(), "UTF-8"));;
                	is = new FileInputStream(classFile);
                	bytes = bind.wrap(IOUtils.toByteArray(is), filter.getMethodNamePattern());
            	} catch (Exception e) {
            		throw new RuntimeException(e);
            	} finally {
            		IOUtils.closeQuietly(is);
            	}
    		}
    	}
    	if (bytes != null) {
    		return this.defineClass(name, bytes, 0, bytes.length);    		
    	} else {
			Class<?> clazz = super.findClass(name);
	        return clazz;
		}
    }
}